/******************************************************************************
 * Copyright 2022 The AIR Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

#include "v2x-asn-msgs-internal.h"

::v2xpb::asn::SpatPhaseColor spat_phase_color_asn2pb(LightState_t light) {
  switch (light) {
    case LightState_dark:
      return v2xpb::asn::COLOR_BLACK;
    case LightState_flashing_red:
      return v2xpb::asn::COLOR_FLASHING_RED;
    case LightState_red:
      return v2xpb::asn::COLOR_RED;
    case LightState_flashing_green:
      return v2xpb::asn::COLOR_FLASHING_GREEN;
    case LightState_permissive_green:
      return v2xpb::asn::COLOR_PERMISSIVE_GREEN;
    case LightState_protected_green:
      return v2xpb::asn::COLOR_PROTECTED_GREEN;
    case LightState_yellow:
      return v2xpb::asn::COLOR_YELLOW;
    case LightState_flashing_yellow:
      return v2xpb::asn::COLOR_FLASHING_YELLOW;
    default:
      return v2xpb::asn::COLOR_UNKNOWN;
  }
}
//
LightState_t spat_phase_color_pb2asn(::v2xpb::asn::SpatPhaseColor color) {
  switch (color) {
    case v2xpb::asn::COLOR_BLACK:
      return LightState_dark;
    case v2xpb::asn::COLOR_FLASHING_RED:
      return LightState_flashing_red;
    case v2xpb::asn::COLOR_RED:
      return LightState_red;
    case v2xpb::asn::COLOR_FLASHING_GREEN:
      return LightState_flashing_green;
    case v2xpb::asn::COLOR_PERMISSIVE_GREEN:
      return LightState_permissive_green;
    case v2xpb::asn::COLOR_PROTECTED_GREEN:
      return LightState_protected_green;
    case v2xpb::asn::COLOR_YELLOW:
      return LightState_yellow;
    case v2xpb::asn::COLOR_FLASHING_YELLOW:
      return LightState_flashing_yellow;
    default:
      return v2xpb::asn::COLOR_UNKNOWN;
  }
}
//
bool spat_phase_state_asn2pb(const PhaseState_t *phase_state_asn,
                             ::v2xpb::asn::SpatPhaseState *phase_state_pb) {
  if (!phase_state_asn || !phase_state_pb) {
    LOG(ERROR) << "NULLPTR";
    return false;
  }
  phase_state_pb->set_color(spat_phase_color_asn2pb(phase_state_asn->light));
  if (!phase_state_asn->timing) {
    return true;
  }
  if (phase_state_asn->timing->present != TimeChangeDetails_PR_counting) {
    LOG(ERROR) << "Unsupported Timing: " << phase_state_asn->timing->present
               << " , Ignored.";
    return false;
  }

  TimeMark_t startTime = phase_state_asn->timing->choice.counting.startTime;
  TimeMark_t likelyEndTime =
      phase_state_asn->timing->choice.counting.likelyEndTime;
  TimeMark_t *nextDuration =
      phase_state_asn->timing->choice.counting.nextDuration;

  phase_state_pb->set_timing_start(0.1 * static_cast<double>(startTime));
  phase_state_pb->set_timing_end(0.1 * static_cast<double>(likelyEndTime));
  if (nextDuration) {
    phase_state_pb->set_timing_duration(0.1 *
                                        static_cast<double>(*nextDuration));
  }
  return true;
}
//

bool spat_phase_state_pb2asn(const ::v2xpb::asn::SpatPhaseState &phase_state_pb,
                             PhaseState_t *phase_state_asn) {
  if (!phase_state_asn) {
    LOG(ERROR) << "NULLPTR";
    return false;
  }
  phase_state_asn->light = spat_phase_color_pb2asn(phase_state_pb.color());
  if (!phase_state_pb.has_timing_start() || !phase_state_pb.has_timing_end()) {
    return true;
  }
  phase_state_asn->timing = ASN1(TimeChangeDetails)::create_tp();

  phase_state_asn->timing->present = TimeChangeDetails_PR_counting;

  auto *counting = &phase_state_asn->timing->choice.counting;
  counting->startTime =
      static_cast<int>(floor(10 * phase_state_pb.timing_start()));
  counting->likelyEndTime =
      static_cast<int>(floor(10 * phase_state_pb.timing_end()));
  if (phase_state_pb.has_timing_duration()) {
    counting->nextDuration = ASN1(TimeMark)::create_tp();
    *counting->nextDuration =
        static_cast<int>(floor(10 * phase_state_pb.timing_duration()));
  }
  return true;
}
//
bool spat_phase_asn2pb(const Phase_t *phase_asn,
                       ::v2xpb::asn::SpatPhase *phase_pb) {
  if (!phase_asn || !phase_pb) {
    LOG(ERROR) << "NULLPTR";
    return false;
  }
  phase_pb->set_id(int32_t(phase_asn->id));
  for (int i = 0; i < phase_asn->phaseStates.list.count; i++) {
    if (!spat_phase_state_asn2pb(phase_asn->phaseStates.list.array[i],
                                 phase_pb->add_phase_state())) {
      LOG(ERROR) << "Failed to convert spat phase state";
      return false;
    }
  }
  return true;
}
bool spat_phase_pb2asn(const ::v2xpb::asn::SpatPhase &phase_pb,
                       Phase_t *phase_asn) {
  if (!phase_asn) {
    LOG(ERROR) << "NULLPTR";
    return false;
  }
  phase_asn->id = phase_pb.id();
  for (const auto &phase_state_pb : phase_pb.phase_state()) {
    ASN_SEQUENCE_ADD(&phase_asn->phaseStates.list,
                     ASN1(PhaseState)::create_tp());
    if (!spat_phase_state_pb2asn(
            phase_state_pb,
            phase_asn->phaseStates.list
                .array[phase_asn->phaseStates.list.count - 1])) {
      LOG(ERROR) << "Failed to convert spat phase state";
      return false;
    }
  }
  return true;
}
//
bool spat_intersection_asn2pb(                    //
    const IntersectionState_t *intersection_asn,  //
    ::v2xpb::asn::SpatIntersection *intersection_pb) {
  if (!intersection_asn || !intersection_pb) {
    LOG(ERROR) << "NULLPTR";
    return false;
  }
  intersection_pb->set_node_id(int32_t(intersection_asn->intersectionId.id));
  if (intersection_asn->intersectionId.region) {
    intersection_pb->set_node_region(
        int32_t(*intersection_asn->intersectionId.region));
  }

  if (nullptr != intersection_asn->moy) {
    intersection_pb->set_moy(int32_t(*intersection_asn->moy));
  }
  if (nullptr != intersection_asn->timeStamp) {
    intersection_pb->set_dsecond(int32_t(*intersection_asn->timeStamp));
  }
  for (int i = 0; i < intersection_asn->phases.list.count; i++) {
    if (!spat_phase_asn2pb(intersection_asn->phases.list.array[i],
                           intersection_pb->add_phases())) {
      LOG(ERROR) << "Failed to convert spat phase";
      return false;
    }
  }
  return true;
}
//
bool spat_intersection_pb2asn(
    const ::v2xpb::asn::SpatIntersection &intersection_pb,
    IntersectionState_t *intersection_asn) {
  if (!intersection_asn) {
    LOG(ERROR) << "NULLPTR";
    return false;
  }
  intersection_asn->intersectionId.id = intersection_pb.node_id();
  if (intersection_pb.has_node_region()) {
    intersection_asn->intersectionId.region =
        ASN1(RoadRegulatorID)::create_tp();
    *intersection_asn->intersectionId.region = intersection_pb.node_region();
  }
  if (intersection_pb.has_moy()) {
    intersection_asn->moy = ASN1(MinuteOfTheYear)::create_tp();
    *intersection_asn->moy = intersection_pb.moy();
  }
  if (intersection_pb.has_dsecond()) {
    intersection_asn->timeStamp = ASN1(DSecond)::create_tp();
    *intersection_asn->timeStamp = intersection_pb.dsecond();
  }
  for (const auto &phase : intersection_pb.phases()) {
    ASN_SEQUENCE_ADD(&intersection_asn->phases.list, ASN1(Phase)::create_tp());
    if (!spat_phase_pb2asn(
            phase, intersection_asn->phases.list
                       .array[intersection_asn->phases.list.count - 1])) {
      LOG(ERROR) << "Failed to convert spat phase";
      return false;
    }
  }
  return true;
}
//
bool spat_asn2pb(const SPAT_t *spat_asn, ::v2xpb::asn::Spat *spat_pb) {
  if (!spat_asn || !spat_pb) {
    LOG(ERROR) << "NULLPTR";
    return false;
  }
  spat_pb->set_message_count(int32_t(spat_asn->msgCnt));
  if (spat_asn->name) {
    spat_pb->set_name(
        std::string((const char *)(spat_asn->name->buf), spat_asn->name->size));
  }
  if (spat_asn->moy) {
    spat_pb->set_moy(int32_t(*spat_asn->moy));
  }
  if (spat_asn->timeStamp) {
    spat_pb->set_dsecond(int32_t(*spat_asn->timeStamp));
  }
  for (int i = 0; i < spat_asn->intersections.list.count; i++) {
    if (!spat_intersection_asn2pb(spat_asn->intersections.list.array[i],
                                  spat_pb->add_intersections())) {
      LOG(ERROR) << "Failed to convert spat intersection";
      return false;
    }
  }
  return true;
}
//
bool spat_pb2asn(const ::v2xpb::asn::Spat &spat_pb, SPAT_t *spat_asn) {
  if (!spat_asn) {
    LOG(ERROR) << "NULLPTR";
    return false;
  }
  spat_asn->msgCnt = spat_pb.message_count();
  if (spat_pb.has_name()) {
    spat_asn->name = ASN1(DescriptiveName)::create_tp();
    OCTET_STRING_fromBuf(spat_asn->name, spat_pb.name().c_str(),
                         static_cast<int>(spat_pb.name().length()));
  }
  if (spat_pb.has_moy()) {
    spat_asn->moy = ASN1(MinuteOfTheYear)::create_tp();
    *spat_asn->moy = spat_pb.moy();
  }
  if (spat_pb.has_dsecond()) {
    spat_asn->timeStamp = ASN1(DSecond)::create_tp();
    *spat_asn->timeStamp = spat_pb.dsecond();
  }
  for (const auto &intersection_pb : spat_pb.intersections()) {
    ASN_SEQUENCE_ADD(&spat_asn->intersections.list,
                     ASN1(IntersectionState)::create_tp());
    if (!spat_intersection_pb2asn(
            intersection_pb,
            spat_asn->intersections.list
                .array[spat_asn->intersections.list.count - 1])) {
      LOG(ERROR) << "Failed to convert spat intersection";
      return false;
    }
  }
  return true;
}
